package com.executor.gateway.controller;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.ReactiveRedisTemplate;
import org.springframework.web.bind.annotation.*;
import reactor.core.publisher.Mono;

import java.time.Duration;
import java.util.HashMap;
import java.util.Map;

/**
 * 响应式熔断器测试控制器
 * 提供熔断器状态查看和控制的测试接口
 * 
 * @author miaoguoxin
 * @date 2024/01/01
 */
@RestController
@RequestMapping("/test/circuit-breaker")
@Slf4j
public class CircuitBreakerTestController {

    @Autowired(required = false)
    private ReactiveRedisTemplate<String, String> redisTemplate;

    private static final String CIRCUIT_BREAKER_KEY_PREFIX = "gateway:circuit_breaker:";
    private static final String FAILURE_COUNT_KEY = ":failure_count";
    private static final String LAST_FAILURE_TIME_KEY = ":last_failure_time";
    private static final String STATE_KEY = ":state";

    private static final String STATE_CLOSED = "CLOSED";
    private static final String STATE_OPEN = "OPEN";
    private static final String STATE_HALF_OPEN = "HALF_OPEN";

    /**
     * 获取指定服务的熔断器状态
     */
    @GetMapping("/status/{serviceId}")
    public Mono<Map<String, Object>> getCircuitBreakerStatus(@PathVariable String serviceId) {
        if (redisTemplate == null) {
            return Mono.just(createErrorResponse("Redis template not available"));
        }

        String circuitBreakerKey = CIRCUIT_BREAKER_KEY_PREFIX + serviceId;

        return Mono.zip(
                redisTemplate.opsForValue().get(circuitBreakerKey + STATE_KEY).defaultIfEmpty(STATE_CLOSED),
                redisTemplate.opsForValue().get(circuitBreakerKey + FAILURE_COUNT_KEY).defaultIfEmpty("0"),
                redisTemplate.opsForValue().get(circuitBreakerKey + LAST_FAILURE_TIME_KEY).defaultIfEmpty("0")
        ).map(tuple -> {
            Map<String, Object> status = new HashMap<>();
            status.put("serviceId", serviceId);
            status.put("state", tuple.getT1());
            status.put("failureCount", Long.parseLong(tuple.getT2()));
            status.put("lastFailureTime", Long.parseLong(tuple.getT3()));
            status.put("timestamp", System.currentTimeMillis());
            return status;
        }).onErrorReturn(createErrorResponse("Failed to get circuit breaker status"));
    }

    /**
     * 获取所有服务的熔断器状态概览
     */
    @GetMapping("/status")
    public Mono<Map<String, Object>> getAllCircuitBreakerStatus() {
        if (redisTemplate == null) {
            return Mono.just(createErrorResponse("Redis template not available"));
        }

        return redisTemplate.keys(CIRCUIT_BREAKER_KEY_PREFIX + "*" + STATE_KEY)
                .collectList()
                .flatMap(keys -> {
                    if (keys.isEmpty()) {
                        Map<String, Object> result = new HashMap<>();
                        result.put("message", "No circuit breakers found");
                        result.put("services", new HashMap<>());
                        return Mono.just(result);
                    }

                    Map<String, Object> allStatus = new HashMap<>();
                    return Mono.fromCallable(() -> {
                        // 简化处理，实际应该并行获取所有状态
                        Map<String, String> services = new HashMap<>();
                        for (String key : keys) {
                            String serviceId = extractServiceIdFromKey(key);
                            services.put(serviceId, "UNKNOWN");
                        }
                        allStatus.put("services", services);
                        allStatus.put("count", services.size());
                        return allStatus;
                    });
                })
                .onErrorReturn(createErrorResponse("Failed to get all circuit breaker status"));
    }

    /**
     * 手动设置熔断器状态
     */
    @PostMapping("/state/{serviceId}")
    public Mono<Map<String, Object>> setCircuitBreakerState(
            @PathVariable String serviceId,
            @RequestParam String state) {
        
        if (redisTemplate == null) {
            return Mono.just(createErrorResponse("Redis template not available"));
        }

        if (!isValidState(state)) {
            return Mono.just(createErrorResponse("Invalid state. Must be OPEN, CLOSED, or HALF_OPEN"));
        }

        String circuitBreakerKey = CIRCUIT_BREAKER_KEY_PREFIX + serviceId;

        return redisTemplate.opsForValue().set(circuitBreakerKey + STATE_KEY, state)
                .then(redisTemplate.expire(circuitBreakerKey + STATE_KEY, Duration.ofHours(1)))
                .then(Mono.fromCallable(() -> {
                    Map<String, Object> result = new HashMap<>();
                    result.put("success", true);
                    result.put("serviceId", serviceId);
                    result.put("newState", state);
                    result.put("timestamp", System.currentTimeMillis());
                    result.put("message", "Circuit breaker state updated successfully");
                    return result;
                }))
                .onErrorReturn(createErrorResponse("Failed to set circuit breaker state"));
    }

    /**
     * 模拟熔断器打开
     */
    @PostMapping("/simulate/open/{serviceId}")
    public Mono<Map<String, Object>> simulateCircuitBreakerOpen(@PathVariable String serviceId) {
        if (redisTemplate == null) {
            return Mono.just(createErrorResponse("Redis template not available"));
        }

        String circuitBreakerKey = CIRCUIT_BREAKER_KEY_PREFIX + serviceId;
        long currentTime = System.currentTimeMillis();

        return redisTemplate.opsForValue().set(circuitBreakerKey + STATE_KEY, STATE_OPEN)
                .then(redisTemplate.expire(circuitBreakerKey + STATE_KEY, Duration.ofHours(1)))
                .then(redisTemplate.opsForValue().set(circuitBreakerKey + FAILURE_COUNT_KEY, "5"))
                .then(redisTemplate.expire(circuitBreakerKey + FAILURE_COUNT_KEY, Duration.ofHours(1)))
                .then(redisTemplate.opsForValue().set(circuitBreakerKey + LAST_FAILURE_TIME_KEY, String.valueOf(currentTime)))
                .then(redisTemplate.expire(circuitBreakerKey + LAST_FAILURE_TIME_KEY, Duration.ofHours(1)))
                .then(Mono.fromCallable(() -> {
                    Map<String, Object> result = new HashMap<>();
                    result.put("success", true);
                    result.put("serviceId", serviceId);
                    result.put("state", STATE_OPEN);
                    result.put("failureCount", 5);
                    result.put("lastFailureTime", currentTime);
                    result.put("message", "Circuit breaker opened successfully for testing");
                    return result;
                }))
                .onErrorReturn(createErrorResponse("Failed to simulate circuit breaker open"));
    }

    /**
     * 模拟熔断器恢复
     */
    @PostMapping("/simulate/recover/{serviceId}")
    public Mono<Map<String, Object>> simulateCircuitBreakerRecover(@PathVariable String serviceId) {
        if (redisTemplate == null) {
            return Mono.just(createErrorResponse("Redis template not available"));
        }

        String circuitBreakerKey = CIRCUIT_BREAKER_KEY_PREFIX + serviceId;

        return redisTemplate.opsForValue().set(circuitBreakerKey + STATE_KEY, STATE_CLOSED)
                .then(redisTemplate.expire(circuitBreakerKey + STATE_KEY, Duration.ofHours(1)))
                .then(redisTemplate.delete(circuitBreakerKey + FAILURE_COUNT_KEY))
                .then(redisTemplate.delete(circuitBreakerKey + LAST_FAILURE_TIME_KEY))
                .then(Mono.fromCallable(() -> {
                    Map<String, Object> result = new HashMap<>();
                    result.put("success", true);
                    result.put("serviceId", serviceId);
                    result.put("state", STATE_CLOSED);
                    result.put("failureCount", 0);
                    result.put("message", "Circuit breaker recovered successfully");
                    return result;
                }))
                .onErrorReturn(createErrorResponse("Failed to simulate circuit breaker recovery"));
    }

    /**
     * 清除指定服务的熔断器数据
     */
    @DeleteMapping("/clear/{serviceId}")
    public Mono<Map<String, Object>> clearCircuitBreakerData(@PathVariable String serviceId) {
        if (redisTemplate == null) {
            return Mono.just(createErrorResponse("Redis template not available"));
        }

        String circuitBreakerKey = CIRCUIT_BREAKER_KEY_PREFIX + serviceId;

        return Mono.when(
                redisTemplate.delete(circuitBreakerKey + STATE_KEY),
                redisTemplate.delete(circuitBreakerKey + FAILURE_COUNT_KEY),
                redisTemplate.delete(circuitBreakerKey + LAST_FAILURE_TIME_KEY)
        ).then(Mono.fromCallable(() -> {
            Map<String, Object> result = new HashMap<>();
            result.put("success", true);
            result.put("serviceId", serviceId);
            result.put("message", "Circuit breaker data cleared successfully");
            return result;
        })).onErrorReturn(createErrorResponse("Failed to clear circuit breaker data"));
    }

    /**
     * 验证状态是否有效
     */
    private boolean isValidState(String state) {
        return STATE_OPEN.equals(state) || STATE_CLOSED.equals(state) || STATE_HALF_OPEN.equals(state);
    }

    /**
     * 从Redis键中提取服务ID
     */
    private String extractServiceIdFromKey(String key) {
        String prefix = CIRCUIT_BREAKER_KEY_PREFIX;
        String suffix = STATE_KEY;
        if (key.startsWith(prefix) && key.endsWith(suffix)) {
            return key.substring(prefix.length(), key.length() - suffix.length());
        }
        return "unknown";
    }

    /**
     * 创建错误响应
     */
    private Map<String, Object> createErrorResponse(String message) {
        Map<String, Object> error = new HashMap<>();
        error.put("success", false);
        error.put("message", message);
        error.put("timestamp", System.currentTimeMillis());
        return error;
    }
}